package com.checknull.druineage.element;

import com.alibaba.druid.util.StringUtils;
import com.checknull.druineage.customizable.UsedForCal;

import java.util.*;

import static com.checknull.druineage.constant.Const.CleanType.*;

public class Column {

    /**
     * 所属表
     */
    private Table owner;

    /**
     * 是否是常量
     */
    private boolean isVal = false;

    /**
     * 是否是未知
     */
    private boolean isUnknown = false;

    /**
     * 字段名
     *
     * @description 如果是常量，值就是name
     */
    private String name;

    /**
     * 源字段
     */
    private List<Column> srcColumns = new ArrayList<>();

    /**
     * 作为源字段时，在血缘中的作用
     * key:目标字段
     * value:作用
     */
    private Map<Column, Set<String>> usedFor = new HashMap<>();

    public Table getOwner() {
        return owner;
    }

    public void setOwner(Table owner) {
        this.owner = owner;
    }

    public boolean isVal() {
        return isVal;
    }

    public void isVal(boolean val) {
        isVal = val;
    }

    public boolean isUnknown() {
        return isUnknown;
    }

    public void isUnknown(boolean unknown) {
        isUnknown = unknown;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Map<Column, Set<String>> getUsedFor() {
        return usedFor;
    }

    public Set<String> getUsedFor(Column column) {
        return usedFor.get(column);
    }

    public void setUsedFor(Map<Column, Set<String>> usedFor) {
        this.usedFor = usedFor;
    }

    public void putUsedFor(Column column, String usedFor) {
        if (column != null && !StringUtils.isEmpty(usedFor)) {
            if (!this.usedFor.containsKey(column)) {
                this.usedFor.put(column, new HashSet<>());
            }
            getUsedFor(column).add(usedFor);
        }
    }

    public void putUsedFor(Column column, Set<String> usedFor) {
        if (column != null && !usedFor.isEmpty()) {
            if (!this.usedFor.containsKey(column)) {
                this.usedFor.put(column, new HashSet<>());
            }
            getUsedFor(column).addAll(usedFor);
        }
    }

    public List<Column> getSrcColumns() {
        return srcColumns;
    }

    public void setSrcColumns(List<Column> srcColumns) {
        this.srcColumns = srcColumns;
    }

    /**
     * 合并并返回源字段
     *
     * @param column 源字段
     * @return 正确的源字段
     * @description 将传入的源字段与现有的源字段集合进行比较，若已存在相同的，则使用现有的源字段
     * 若不存在，则添加进源字段集合，整理完之后再返回正确的源字段
     */
    public Column absorbSrcColumn(Column column) {
        if (StringUtils.isEmpty(column.getName()) && !column.isVal()) {
            return null;
        }
        if (column.isVal()) {
            for (Column srcColumn : srcColumns) {
                if (srcColumn.isVal()
                        && ((column.getName() == null && srcColumn.getName() == null)
                        || (column.getName() != null && srcColumn.getName() != null
                        && column.getName().equalsIgnoreCase(srcColumn.getName())))) {
                    return srcColumn;
                }
            }
        } else {
            for (Column srcColumn : srcColumns) {
                if (column.getName().equalsIgnoreCase(srcColumn.getName())) {
                    if (column.getOwner() == null && srcColumn.getOwner() == null) {
                        return srcColumn;
                    } else if (column.getOwner() != null && srcColumn.getOwner() != null && column.getOwner().getName().equalsIgnoreCase(srcColumn.getOwner().getName())) {
                        if (column.getOwner().getOwner() == null && srcColumn.getOwner().getOwner() == null) {
                            return srcColumn;
                        } else if (column.getOwner().getOwner() != null && srcColumn.getOwner().getOwner() != null) {
                            if ((column.getOwner().getOwner().getName() == null && srcColumn.getOwner().getOwner().getName() == null) || (column.getOwner().getOwner().getName() != null && srcColumn.getOwner().getOwner().getName() != null && column.getOwner().getOwner().getName().equalsIgnoreCase(srcColumn.getOwner().getOwner().getName()))) {
                                return srcColumn;
                            }
                        }
                    }
                }
            }
        }
        srcColumns.add(column);
        return column;
    }

    /**
     * 字段吸收
     *
     * @param column 被吸收字段
     * @return 吸收完成后的字段
     * @description 吸收并不覆盖，优先保留自有的。吸收内容：传入字段的所有源字段和血缘作用
     */
    public Column absorb(Column column) {
        for (Column srcColumn : column.getSrcColumns()) {
            Column thisSrcColumn = absorbSrcColumn(srcColumn);
            if (!srcColumn.equals(thisSrcColumn) && thisSrcColumn.getOwner() != null) {
                thisSrcColumn.owner.absorb(srcColumn.owner);
            }
        }
        usedFor.putAll(column.getUsedFor());
        return this;
    }

    /**
     * 清理中间表（临时表、删除表、即是源表又是目标表的表）
     *
     * @param cleanType  清理类型
     * @param usedForCal 血缘作用计算器
     * @description 清理中间表时，源字段会被删除，源字段的源字段会成为源字段
     * 新源字段的血缘作用是由原源字段和现源字段（原源字段的源字段）的血缘作用计算而得。计算方式可自定义
     * "清理类型"原先准备用"清理级别"，但是级别有越界的趋势，导致结果有问题
     */
    public void clearMidTable(int cleanType, UsedForCal usedForCal) {
        if (srcColumns.stream().noneMatch(col -> col.getOwner() != null
                && (cleanType == TEMP && col.getOwner().isTemp()
                || (cleanType == DROP && col.getOwner().isDrop())
                || (cleanType == MID && !col.getSrcColumns().isEmpty())))) {
            return;
        }
        for (int i = 0; i < srcColumns.size(); i++) {
            Column srcColumn = srcColumns.get(i);
            if (srcColumn.getOwner() != null
                    && (cleanType == TEMP && srcColumn.getOwner().isTemp()
                    || (cleanType == DROP && srcColumn.getOwner().isDrop())
                    || (cleanType == MID && !srcColumn.getSrcColumns().isEmpty()))) {
                srcColumns.remove(i);
                i--;
                Map<Column, Set<String>> usedForMap = srcColumn.getUsedFor();
                Set<String> usedFor = usedForMap.get(this);
                for (Column childSrcColumn : srcColumn.getSrcColumns()) {
                    absorbSrcColumn(childSrcColumn);
                    Map<Column, Set<String>> childUsedForMap = childSrcColumn.getUsedFor();
                    Set<String> childUsedFor = childUsedForMap.get(srcColumn);
                    childUsedForMap.put(this, usedForCal.calculate(usedFor, childUsedFor));
                }
            }
        }
        clearMidTable(cleanType, usedForCal);
    }

    /**
     * 清理临时表
     *
     * @param usedForCal
     */
    public void clearTempTable(UsedForCal usedForCal) {
        clearMidTable(TEMP, usedForCal);
    }

    public void clearDropTable(UsedForCal usedForCal) {
        clearMidTable(DROP, usedForCal);
    }

    public void clearMidTable(UsedForCal usedForCal) {
        clearMidTable(MID, usedForCal);
    }

    @Override
    public String toString() {
        return StringUtils.isEmpty(name) ? super.toString() : name;
    }

}
